package com.hotdog.agent;


import org.apache.log4j.Logger;
import org.objectweb.asm.ClassReader;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;

/**
 * 热部署 agent
 *
 * @Author wangzhengnian
 * @Date 2021/3/16 09:25
 * @Version 1.0
 */
public class HotdogAgentMain {


    private static final Logger LOGGER = Logger.getLogger(HotdogAgentMain.class);

    public static void agentmain(String arg, Instrumentation instrumentation) throws UnmodifiableClassException, ClassNotFoundException {
        String[] args = arg.split(",");
        String classPath = args[0];

        // 获取class文件的路径
        LOGGER.info("load class path：" + classPath);
        // 获取class文件
        byte[] bytes = getClassFileToByte(classPath);
        if (bytes == null) {
            return;
        }
        // 通过asm获取类名
        String clazzName = args[1];
        LOGGER.info("the current replacement class name is " + clazzName);
        boolean replaceState = false;
        //替换类
        for (Class<?> clazz : instrumentation.getAllLoadedClasses()) {
            // 匹配需要替换 class
            if (!clazz.getName().equals(clazzName)) {
                continue;
            }
            ClassDefinition definition = new ClassDefinition(clazz, bytes);
            // 替换内存中的类二进制
            instrumentation.redefineClasses(definition);
            replaceState = true;
        }
        if (replaceState) {
            LOGGER.info("class loaded successfully");
        } else {
            LOGGER.error("failed to replace class，unknown class");
        }
    }

    private static byte[] getClassFileToByte(String classPath) {
        byte[] bytes;
        RandomAccessFile randomAccessFile = null;
        try {
            randomAccessFile = new RandomAccessFile(classPath, "r");
            bytes = new byte[(int) randomAccessFile.length()];
            randomAccessFile.readFully(bytes);
            return bytes;
        } catch (IOException e) {
            LOGGER.error("load class file error", e);
            return null;
        } finally {
            if (randomAccessFile != null) {
                try {
                    randomAccessFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 使用 asm 读取类名
     *
     * @param bytes 文件二进制流
     * @return class名
     */
    private static String readClassName(byte[] bytes) {
        return new ClassReader(bytes).getClassName().replace("/", ".");
    }

    public static void agentmain(String agentArgs) {
        System.out.println("=========agentmain(String agentArgs) test=========");
        System.out.println(agentArgs);
    }

}
